#cython:language_level = 3
import pygame
import config
#import numpy as np
from cython.view cimport array as cvarray

cdef long cint(float d):
    return <long>d  #d if type(d) is int else int(d)

cdef class Sun(object):
    cdef public object darkOrigin
    cdef public object lightOrigin
    cdef readonly object scr
    def __init__(self, Display) -> None:
        self.scr = Display
        self.darkOrigin = pygame.Surface((1650, 40))
        self.darkOrigin.fill((0, 0, 0))
        self.lightOrigin = pygame.Surface((1650, 40))
        self.lightOrigin.fill((2, 2, 2))
    
    cpdef bint draw(self, int dark = -1):
        cdef int row
        cdef int darkness
        if dark < -1:
            return False
        elif dark > 120:
            return False
        else:
            for row in range(0, 23):
                darkness = cint(<float>dark / 2 + <float>row * 2.5 + 1)
                self.darkOrigin.fill((darkness, darkness, darkness))
                self.scr.blit(self.darkOrigin, (0, 40*row), special_flags=pygame.BLEND_RGB_SUB)
            return True

cpdef int mid(int x0, int a = 0, int b = 15):
    cdef int x = x0
    if a <= x <= b:
        return x
    if x < a:
        x = a
    elif x > b:
        x = b
    return x

cdef int intmin(int x1, int x2):
    return x1 if x1 <= x2 else x2

cdef class light_block(object):
    cdef public int x
    cdef public int y
    cdef public int light
    def __init__(self, int x, int y, int light):
        self.x = x
        self.y = y
        self.light = light

cdef class Ray_Tracing(object):
    cdef int[:,:] lightmap
    cdef object origin
    cdef bint first_run
    cdef object scr
    cdef list lightpics
    #def __cinit__(self, Display, bint Smooth_lighting=False, int gamma = 6):
        #self.lightmap0= <int**>malloc(23 * sizeof(int*))
        #for i in range(23):
        #    self.lightmap0[i] = <int*>malloc(42 * sizeof(int))
        #    memset(self.lightmap0[i], 0, 42 * sizeof(int))
        
    def __init__(self, Display, bint Smooth_lighting = False, int gamma = 6):
        self.scr = Display
        self.origin = pygame.Surface((40, 40))  # if not Smooth_lighting else (20, 20)
        self.lightmap = cvarray(shape=(23, 42), itemsize=sizeof(int), format="i")
        #for i in range(23):
        #    for y in range(42):

        #self.lightmap = np.zeros((23, 42), dtype=np.intc)
        self.lightpics = [] # dark: max->min
        cdef int i
        for i in range(15):
            pic = pygame.Surface((40, 40))
            pic.fill((i*gamma, i*gamma, i*gamma))
            self.lightpics.append(pic)

        #print('成功初始化')
        self.first_run = config.DEBUG
    
    
    cpdef void draw(self, int dark = -1, tuple lights = ()):
        cdef int row
        cdef int col
        cdef light_block i
        cdef int j
        cdef int x
        cdef int y
        cdef int x0
        cdef int y0
        cdef int light_now
        if dark == -1:
            dark = 0
        dark = cint(<float>dark * 15 / 120)
        dark = mid(dark, 0, 15)
        if self.first_run == True:
            print('enter func')
        for row in range(23):
            for col in range(42):
                self.lightmap[row, col] = mid(cint(dark + <float>row*0.6), 0, 15)
        if self.first_run == True and config.stdio == True:
            print('normal light')
        if lights == ():
            pass
        else:
            for i in lights:
                x, y = mid(cint(<float>i.x / 40), 0, 41), mid(cint(<float>i.y / 40), 0, 22)
                self.lightmap[y, x] = min(15 - i.light, self.lightmap[y, x])
                if self.first_run == True and config.stdio == True:
                    print('ok 1')
                for j in range(i.light - 1):
                    x0 = cint(<float>i.x/40) - j - 1
                    y0 = cint(<float>i.y/40)
                    light_now = 15 - (i.light - j)
                    # 顺时针绕行, 逐个改变亮度值
                    if self.first_run == True and config.stdio == True:
                        print(f'check{j} x0={x0},y0={y0} light={light_now}')
                    while x0 < cint(i.x/40):
                        if 0<= x0 <=41 and 0<= y0 <=22:
                            self.lightmap[y0, x0] = min(light_now, self.lightmap[y0, x0])
                        x0 += 1
                        y0 -= 1
                    if self.first_run == True and config.stdio == True:
                        print(f'check{j} x0={x0},y0={y0} light={light_now}')
                    while y0 < cint(i.y/40):
                        if 0<= x0 <=41 and 0<= y0 <=22:
                            self.lightmap[y0, x0] = min(light_now, self.lightmap[y0, x0])
                        x0 += 1
                        y0 += 1
                    if self.first_run == True and config.stdio == True:
                        print(f'check{j} x0={x0},y0={y0} light={light_now}')
                    while x0 > cint(i.x/40):
                        if 0<= x0 <=41 and 0<= y0 <=22:
                            self.lightmap[y0, x0] = min(light_now, self.lightmap[y0, x0])
                        x0 -= 1
                        y0 += 1
                    if self.first_run == True and config.stdio == True:
                        print(f'check{j} x0={x0},y0={y0} light={light_now}')
                    while y0 > cint(i.y/40):
                        if 0<= x0 <=41 and 0<= y0 <=22:
                            self.lightmap[y0, x0] = min(light_now, self.lightmap[y0, x0])
                        x0 -= 1
                        y0 -= 1
                    if self.first_run == True and config.stdio == True:
                        print(f'check{j} x0={x0},y0={y0} light={light_now}')
        if self.first_run == True and config.stdio == True:
            print('high light')
        # render
        for row in range(23):
            for col in range(42):
                if self.first_run == True and col == 0 and config.stdio == True:
                    print(f'rendering in row{row}')
                    if row == 2:
                        print(f'rendering row 2 -- col {col}, lightmap = {self.lightmap[row][col]}')
                self.scr.blit(self.lightpics[mid(self.lightmap[row, col], 0, 14)], (col*40, row*40), special_flags=pygame.BLEND_RGB_SUB)
        if self.first_run == True:
            if config.stdio == True:
                print('render check finish')
            self.first_run = False
    
    cpdef int get_client(self, x:int, y:int):
        cdef int x0
        cdef int y0
        x0, y0 = mid(cint(x/40), 0, 41), mid(cint(y/40), 0, 22)
        return 15 - self.lightmap[y0, x0]

"""
cdef class RayTracing:
    cdef int[:,:] lightmap
    cdef pygame.surface.Surface fillpic
    cdef pygame.surface.Surface scr
    cdef int gamma
    def __init__(self, pygame.surface.Surface Display, int gamma = 6):
        self.lightmap = cvarray(shape=(920, 1650), itemsize=sizeof(int), format="i")
        self.scr = Display
        self.fillpic = pygame.Surface((1650, 920))
        self.gamma = gamma
    
    cpdef void draw(self, int dark = -1, tuple lights = ()):
        cdef:
            int row
            int col
            int x
            int y
            int colorvalue
            light_block i
        if dark == -1:
            dark = 0
        dark = cint(dark * 15 / 120)
        dark = mid(dark, 0, 15)
        for row in range(920):
            for col in range(1650):
                colorvalue = mid(cint(dark * self.gamma + <float>row * self.gamma / 80), 0, 15*self.gamma)
                self.lightmap[row, col] = colorvalue
                self.fillpic.set_at((col, row), (colorvalue, colorvalue, colorvalue))
        if lights == ():
            pass
        else:
            for i in lights:
                x, y = i.x, i.y
                colorvalue = intmin(cint((15 - i.light) * self.gamma), self.lightmap[y, x])
                self.lightmap[y, x] = colorvalue
"""